package pkg

import (
	"fmt"
	"math"
	"net/http"
	"reflect"
	"regexp"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	zhTranslations "github.com/go-playground/validator/v10/translations/zh"
	"github.com/pkg/errors"

	"gitee.com/ltotal/ppw_gin/errs"
)

type debugResponse struct {
	Code      int         `json:"code"`
	Data      interface{} `json:"data"`
	Msg       string      `json:"msg"`
	Sqls      []string    `json:"sqls"`
	Cause     string      `json:"cause"`
	Reference string      `json:"reference"`
	Stack     []string    `json:"stack"`
}

type response struct {
	Code int         `json:"code"`
	Data interface{} `json:"data"`
	Msg  string      `json:"msg"`
}

type Rule struct {
	Method      string
	Route       string
	Handler     interface{}
	DataBind    binding.Binding
	Middlewares []gin.HandlerFunc
}

type Routes struct {
	Group       string
	Rules       []Rule
	Middlewares []gin.HandlerFunc
}

type Validators struct {
	Tag     string
	RegExp  string
	Handler func(fl validator.FieldLevel) bool
	Msg     string
}

type APIPkg struct {
	validate *validator.Validate
	trans    ut.Translator
}

var API = &APIPkg{}

func (p *APIPkg) RegisterRoutes(r *gin.Engine, routes ...Routes) {
	p.initValidator()
	for _, route := range routes {
		group := r.Group(route.Group, route.Middlewares...) // 添加组中间件
		for _, rule := range route.Rules {
			var handler gin.HandlerFunc
			if rule.Handler != nil {
				handler = p.bindHandler(rule.Handler, rule.DataBind)
			} else {
				handler = func(c *gin.Context) {
					c.JSON(http.StatusOK, response{http.StatusInternalServerError, struct{}{}, "Handler not implemented"})
				}
			}

			if len(rule.Middlewares) > 0 {
				// 如果规则中有中间件，使用这些中间件包裹处理器
				group.Handle(rule.Method, rule.Route, append(rule.Middlewares, handler)...)
			} else {
				// 否则，只使用处理器
				group.Handle(rule.Method, rule.Route, handler)
			}
		}
	}
}

func (p *APIPkg) RegisterValidates(validates []Validators) {
	for _, v := range validates {
		if v.RegExp != "" {
			p.addRegValidator(v.Tag, v.RegExp, v.Msg)
		} else if v.Handler != nil {
			p.addFuncValidator(v.Tag, v.Handler, v.Msg)
		}
	}
}

func (p *APIPkg) GetResponse(data interface{}, err error, sqls ...string) (int, interface{}) {
	code, statusCode, msg, cause, reference := http.StatusOK, http.StatusOK, "", "", ""
	if sqls == nil {
		sqls = []string{}
	}
	var stackSlice []string

	if err != nil {
		data = struct{}{}
		if apiErr := errs.AsAPICoder(err); apiErr != nil {
			code = apiErr.Code()
			if valid := p.isValidErrorCode(code); valid {
				statusCode = p.extractHTTPStatusCode(code)
			}
			msg, cause, reference = apiErr.Message(), errs.Cause(err).Error(), apiErr.Reference()
		} else {
			code, msg = http.StatusBadRequest, err.Error()
		}
	}

	if gin.IsDebugging() {
		stackSlice = p.extractStackTrace(err)
		return statusCode, debugResponse{code, data, msg, sqls, cause, reference, stackSlice}
	}
	return statusCode, response{code, data, msg}
}

func (p *APIPkg) bindHandler(f interface{}, bind binding.Binding) gin.HandlerFunc {
	if f == nil {
		return func(c *gin.Context) {
			c.JSON(http.StatusOK, response{http.StatusOK, struct{}{}, "Handler not implemented"})
		}
	}

	t := reflect.TypeOf(f)
	if t.Kind() != reflect.Func || t.NumIn() != 2 || t.In(0) != reflect.TypeOf(&gin.Context{}) || t.NumOut() != 2 || !t.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
		panic("handler must be a method and conform to the required request parameters and return values")
	}

	return func(c *gin.Context) {
		var req interface{}
		if bind != nil {
			req = p.newReqInstance(t.In(1))
			if err := c.ShouldBindWith(req, bind); err != nil {
				c.JSON(http.StatusOK, response{http.StatusBadRequest, struct{}{}, p.formatBindError(err)})
				return
			}
		} else {
			req = reflect.New(t.In(1)).Interface()
		}

		res := reflect.ValueOf(f).Call([]reflect.Value{reflect.ValueOf(c), reflect.ValueOf(req)})
		resErr, _ := res[1].Interface().(error)
		c.JSON(p.GetResponse(res[0].Interface(), resErr, p.getSQLs(c)...))
	}
}

func (p *APIPkg) addRegValidator(tagName, regStr, msg string) {
	if err := p.validate.RegisterValidation(tagName, func(fl validator.FieldLevel) bool {
		return regexp.MustCompile(regStr).MatchString(fl.Field().String())
	}); err != nil {
		panic(err)
	}
	p.updateTranslation(tagName, msg)
}

func (p *APIPkg) addFuncValidator(tagName string, validateFunc func(fl validator.FieldLevel) bool, msg string) {
	if err := p.validate.RegisterValidation(tagName, validateFunc); err != nil {
		panic(err)
	}
	p.updateTranslation(tagName, msg)
}

func (p *APIPkg) initValidator() {
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		p.validate = v
	} else {
		panic("failed to assert validator engine to *validator.Validate")
	}
	uni := ut.New(zh.New(), zh.New())
	p.trans, _ = uni.GetTranslator("zh")
	if err := zhTranslations.RegisterDefaultTranslations(p.validate, p.trans); err != nil {
		panic(err)
	}
}

func (p *APIPkg) formatBindError(err error) string {
	tErrs := p.translate(err)
	if len(tErrs) > 0 {
		return tErrs[0]
	}
	matches := regexp.MustCompile(`strconv\.Parse\w+: parsing "(\w+)"`).FindStringSubmatch(err.Error())
	if len(matches) > 1 {
		return fmt.Sprintf("request field type: %s", matches[1])
	}
	return "api check error"
}

func (p *APIPkg) newReqInstance(t reflect.Type) interface{} {
	if t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface {
		return p.newReqInstance(t.Elem())
	}
	return reflect.New(t).Interface()
}

func (p *APIPkg) translate(tErr error) []string {
	var result []string
	if errs, ok := tErr.(validator.ValidationErrors); ok {
		for _, err := range errs {
			result = append(result, err.Translate(p.trans))
		}
	}
	return result
}

func (p *APIPkg) updateTranslation(tagName, msg string) {
	if err := p.validate.RegisterTranslation(tagName, p.trans, func(ut ut.Translator) error {
		if err := ut.Add(tagName, msg, true); err != nil {
			return fmt.Errorf("failed to add translation for tag %s: %w", tagName, err)
		}
		return nil
	}, func(ut ut.Translator, fe validator.FieldError) string {
		t, _ := ut.T(fe.Tag(), fe.Field())
		return t
	}); err != nil {
		panic(err)
	}
}

func (p *APIPkg) isValidErrorCode(code int) bool {
	return code >= 10000000 && code <= 99999999
}

func (p *APIPkg) extractHTTPStatusCode(code int) int {
	if valid := p.isValidErrorCode(code); valid {
		return code / int(math.Pow10(5))
	}
	return http.StatusOK
}

func (p *APIPkg) extractStackTrace(err error) []string {
	var stackSlice []string
	stackErr := errors.WithStack(err)
	strStack := strings.TrimSpace(errs.StackTrace(stackErr))
	if strStack != "" {
		stackSlice = strings.Split(strStack, "\n")
		for i, line := range stackSlice {
			stackSlice[i] = strings.TrimSpace(line)
		}
	}
	return stackSlice
}

func (p *APIPkg) getSQLs(c *gin.Context) []string {
	if sqls, ok := c.Get("sqls"); ok {
		if sqlsList, ok2 := sqls.([]string); ok2 {
			return sqlsList
		}
		panic("failed to assert sqls to []string")
	}
	return []string{}
}
